﻿using System;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Actya.Core;
using Actya.Core.Extensibility;
using Actya.Core.Infrastructure;
using Actya.Core.Infrastructure.Mef;
using Actya.Core.SiteStructure;
using Actya.Web.Mvc.Helpers;
using Actya.Web.ViewModels.Admin;

namespace Actya.Web.Controllers.CmsAdmin
{
	[Authorize(Roles = RoleNames.CmsAdministrators)]
	public class PluginsController : Controller
	{
		private readonly ICmsContext _cmsContext;
		private readonly ISiteService _siteService;

		public PluginsController(ICmsContext cmsContext, ISiteService siteService)
		{
			_cmsContext = cmsContext;
			_siteService = siteService;
		}

		public ActionResult Index()
		{
			var viewModel = new PluginsViewModel();
			var allAvailablePlugins = this._cmsContext.Host.GetAvailablePluginTypes();
			viewModel.InstalledPlugins = this._cmsContext.Site.PluginTypes;
			var installedPlugins = viewModel.InstalledPlugins.Select(t => t.Name);
			viewModel.AvailablePlugins = allAvailablePlugins.Where(t => !installedPlugins.Contains(t.Name));
			return View(viewModel);
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		public ActionResult Install(string name)
		{
			var host = _cmsContext.Host;
			try
			{
				var pluginType = host.GetAvailablePluginTypes().Single(t => t.Name == name);

				var pluginDirectory = Path.Combine(host.PhysicalPluginsDirectory, pluginType.Name);
				var pluginBinDirectory = Path.Combine(pluginDirectory, "bin");
				var catalog = new PluginDirectoryCatalog(pluginBinDirectory);
				using (var tempContainer = new CompositionContainer(catalog))
				{
					foreach (var plugin in tempContainer.GetExportedValues<IPlugin>())
					{
						plugin.Install(this._cmsContext);
					}
				}

				// Install plugin files in the Themes/Default folder
				var destinationPath = Path.Combine(host.PhysicalThemesDirectory, "Default");
				CopyPluginFiles(pluginDirectory, destinationPath);

				var site = _cmsContext.Site;
				site.InstallPluginType(pluginType);
				_siteService.UpdateSite(site);
				
				host.RestartApplication(this.HttpContext);
			}
			catch (Exception ex)
			{ 
				this.ShowMessage(MessageType.Error, ex.Message, true);
			}
			return RedirectToAction("Index");
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		public ActionResult Uninstall(string name)
		{
			try
			{
				var site = _cmsContext.Site;
				var pluginType = site.PluginTypes.Single(t => t.Name == name);

				var host = _cmsContext.Host;
				var pluginDirectory = Path.Combine(host.PhysicalPluginsDirectory, pluginType.Name);
				var pluginBinDirectory = Path.Combine(pluginDirectory, "bin");
				var catalog = new PluginDirectoryCatalog(pluginBinDirectory);
				using (var tempContainer = new CompositionContainer(catalog))
				{
					foreach (var plugin in tempContainer.GetExportedValues<IPlugin>())
					{
						plugin.Uninstall(this._cmsContext);
					}
				}

				// Remove plugin files
				var installationDirectory = Path.Combine(host.PhysicalThemesDirectory, "Default");
				DeletePluginFiles(pluginDirectory, installationDirectory);

				site.UninstallPluginType(pluginType);
				_siteService.UpdateSite(site);

				host.RestartApplication(this.HttpContext);
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message, true);
			}
			return RedirectToAction("Index");
		}

		private void CopyPluginFiles(string sourcePath, string destinationPath)
		{
			// Copy directories
			foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)
				.Where(x => !x.Contains(Path.DirectorySeparatorChar + "bin")))
			{
				var directory = dirPath.Replace(sourcePath, destinationPath);
				if (!Directory.Exists(directory))
				{
					Directory.CreateDirectory(directory);
				}
			}
			// Copy all the files
			foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories)
				.Where(x => !x.Contains(Path.DirectorySeparatorChar + "bin") && ! x.EndsWith(".config") && ! x.EndsWith(".json")))
			{
				System.IO.File.Copy(newPath, newPath.Replace(sourcePath, destinationPath), true);
			}
		}

		private void DeletePluginFiles(string pluginDirectory, string installationDirectory)
		{
			// Remove files
			foreach (string installedFile in Directory.GetFiles(pluginDirectory, "*.*", SearchOption.AllDirectories)
				.Where(x => !x.Contains(Path.DirectorySeparatorChar + "bin") && !x.EndsWith(".config") && !x.EndsWith(".json")))
			{
				var fileToRemove = installedFile.Replace(pluginDirectory, installationDirectory);
				if (System.IO.File.Exists(fileToRemove))
				{
					System.IO.File.Delete(fileToRemove);
				}
			}

			// Remove empty directories
			foreach (string dirPath in Directory.GetDirectories(pluginDirectory, "*", SearchOption.AllDirectories)
				.Where(x => !x.Contains(Path.DirectorySeparatorChar + "bin")))
			{
				var directory = dirPath.Replace(pluginDirectory, installationDirectory);
				if (Directory.Exists(directory) && ! Directory.GetFiles(directory).Any())
				{
					Directory.Delete(directory);
				}
			}
		}
	}
}
